package com.darkidiot.session.serialize;

import com.darkidiot.session.exception.SerializableException;
import com.google.common.base.Throwables;
import com.google.common.collect.Maps;
import com.google.gson.*;
import com.google.gson.reflect.TypeToken;
import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * Json序列化支持类
 * session attribute持久化到redis
 * Copyright (c) for darkidiot
 * Date:2017/4/14
 * Author: <a href="darkidiot@icloud.com">darkidiot</a>
 * School: CUIT
 * Desc:
 */
@Slf4j
public class JsonSerializer implements Serializer {
    private final Gson gson;
    private final Type typeToken;


    public JsonSerializer() {
        this.gson = new GsonBuilder()
                .registerTypeAdapter(
                        new TypeToken<HashMap<String, Object>>() {
                        }.getType(),
                        new JsonDeserializer<HashMap<String, Object>>() {
                            @Override
                            public HashMap<String, Object> deserialize(
                                    JsonElement json, Type typeOfT,
                                    JsonDeserializationContext context) throws JsonParseException {

                                HashMap<String, Object> map = Maps.newHashMap();
                                JsonObject jsonObject = json.getAsJsonObject();
                                Set<Map.Entry<String, JsonElement>> entrySet = jsonObject.entrySet();
                                for (Map.Entry<String, JsonElement> entry : entrySet) {
                                    Object ot = entry.getValue();
                                    if (ot instanceof JsonPrimitive) {
                                        map.put(entry.getKey(), ((JsonPrimitive) ot).getAsString());
                                    } else {
                                        map.put(entry.getKey(), ot);
                                    }
                                }
                                return map;
                            }
                        }).create();
        this.typeToken = new TypeToken<HashMap<String, Object>>() {
        }.getType();
    }

    @Override
    public String serialize(Map<String, Object> o) {
        try {
            return gson.toJson(o);
        } catch (Exception e) {
            log.error("failed to serialize http session {} to json,cause:{}", o, Throwables.getStackTraceAsString(e));
            throw new SerializableException(SerializableException.Code.SERIALIZE_ERROR, "failed to serialize http session to json", e);
        }
    }

    @Override
    public Map<String, Object> deserialize(String o) {
        try {
            return gson.fromJson(o, typeToken);
        } catch (Exception e) {
            log.error("failed to deserialize string  {} to http session,cause:{} ", o, Throwables.getStackTraceAsString(e));
            throw new SerializableException(SerializableException.Code.DESERIALIZE_ERROR, "failed to deserialize string to http session", e);
        }
    }
}
